package com.amon.canary;

import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import java.lang.ref.ReferenceQueue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class Watcher {
    private static Watcher mInstance;
    private final ReferenceQueue<Object> mQueue;
    private final Handler mBackgroundHandler;
    private final Set<String> retainedKeys;

    private Watcher() {
        mQueue = new ReferenceQueue<>();
        HandlerThread handlerThread = new HandlerThread("watcher");
        handlerThread.start();
        mBackgroundHandler = new Handler(handlerThread.getLooper());
        retainedKeys = new CopyOnWriteArraySet<>();
    }

    public static Watcher getInstance() {
        if (mInstance == null) {
            synchronized (Watcher.class) {
                if (mInstance == null) {
                    mInstance = new Watcher();
                }
            }
        }
        return mInstance;
    }

    public void addWatch(Object object) {
        String key = UUID.randomUUID().toString();
        KeyWeakReference weakReference = new KeyWeakReference(key, object, mQueue);
        retainedKeys.add(key);
        mBackgroundHandler.postDelayed(() -> checkRelease(weakReference), 5000);
    }

    private void checkRelease(KeyWeakReference weakReference) {
        removeWeakReference();
        if (isRelease(weakReference)) {
            return;
        }
        Runtime.getRuntime().gc();
        System.runFinalization();
        removeWeakReference();
        if (!isRelease(weakReference)) {
            Log.e("TAG", "有内存泄漏:" + weakReference.get());
            HeapAnalyzer heapAnalyzer = new HeapAnalyzer();
            heapAnalyzer.analysisLeak(weakReference.key);
        }
    }

    private void removeWeakReference() {
        KeyWeakReference reference;
        while ((reference = (KeyWeakReference) mQueue.poll()) != null) {
            retainedKeys.remove(reference.key);
        }
    }

    private boolean isRelease(KeyWeakReference weakReference) {
        return !retainedKeys.contains(weakReference);
    }
}
